package com.kadron.basket;

import java.io.Serializable;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;

import javax.transaction.UserTransaction;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.Create;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.contexts.Contexts;
import org.jboss.seam.core.Interpolator;
import org.jboss.seam.international.StatusMessage.Severity;
import org.jboss.seam.transaction.Transaction;

import com.kadron.common.KConst;
import com.kadron.common.KUtil;
import com.kadron.dao.KCommonDAO;
import com.kadron.dao.KDiscountDAO;
import com.kadron.dao.KDiscountItemDAO;
import com.kadron.dao.KImageDAO;
import com.kadron.dao.KPriceDAO;
import com.kadron.dao.KUserKadronsDAO;
import com.kadron.entity.Currency;
import com.kadron.entity.Discount;
import com.kadron.entity.Item;
import com.kadron.entity.ItemColor;
import com.kadron.entity.ItemMaterial;
import com.kadron.entity.ItemSize;
import com.kadron.entity.KItemImage;
import com.kadron.entity.Order;
import com.kadron.entity.OrderItem;
import com.kadron.entity.Price;
import com.kadron.entity.User;
import com.kadron.enumeration.UserStatus;
import com.kadron.enumeration.UserType;
import com.kadron.mail.KMailService;
import com.kadron.order.OrderItemPrice;
import com.kadron.service.KUserService;
import com.kadron.service.UserDiscountService;
import com.kadron.service.UserHistoryService;
import com.kadron.session.AbstractCurrencySelector;
import com.kadron.session.Basket;
import com.kadron.session.BasketItem;
import com.kadron.session.KUser;

@AutoCreate
@Name("kBasketManager")
@Scope(ScopeType.PAGE)
public class KBasketManager extends AbstractCurrencySelector implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = 7978046928863041847L;
	
	private static final Log log = LogFactory.getLog(KBasketManager.class);

	private List<BasketItemData> list = new LinkedList<BasketItemData>();
	private BigDecimal total = new BigDecimal(0);
	private long totalKadrons;
	private Currency currency;
	private Discount kadronDiscount;
	private int userDiscount;
	
	@In
	private KDiscountDAO discountDAO;
	@In
	private KDiscountItemDAO discountItemDAO;
	@In
	private KPriceDAO priceDAO;
	@In
	private KImageDAO imageDAO;
	@In
	private KCommonDAO commonDAO;
	@In
	private KUserKadronsDAO userKadronsDAO;
	@In
    private UserHistoryService userHistoryService;
	@In
    private KUserService userService;
	
	@Create
	public void init() {
		reset();
		KUser kUser = KUser.instance();
		if (kUser != null) {
			currency = kUser.getCurrency();
			kadronDiscount = discountDAO.getKadronDiscount(KUtil.getMoment());
			if (kUser.getUser() != null) {
				userDiscount = UserDiscountService.getDiscount(kUser.getUser());				
			}
			Basket basket = kUser.getBasket();
			for (BasketItem bi : basket.getItems()) {
				BasketItemData data = create(bi);
				list.add(data);
			}
			calculateTotal();
		} else {
			setError(true);
		}
	}
	
	private void calculateTotal() {
		total = new BigDecimal(0);
		totalKadrons = 0;
		for (BasketItemData data : list) {
			totalKadrons += (data.getItem().getKadrons() * data.getQuantity());
			OrderItemPrice price = data.getPrice();
			if (price != null) {
				BigDecimal sum = data.getSum();
				total = total.add(sum);
			}
		}
	}
	
	private void reset() {
		KUser kUser = KUser.instance();
		list.clear();
		total = new BigDecimal(0);
		totalKadrons = 0;
		currency = kUser.getCurrency();
	}
	
	public List<BasketItemData> getList() {
		return list;
	}
	
	public BigDecimal getTotal() {
		return total;
	}

	public Currency getCurrency() {
		return currency;
	}

	private BasketItemData create(BasketItem bi) {
		Date moment = KUtil.getMoment();
		Item item = commonDAO.find(bi.getItemId(), Item.class);
		Discount discount = discountItemDAO.getDiscount(item.getId(), moment);
		ItemColor itemColor = commonDAO.find(bi.getItemColorId(), ItemColor.class);
		ItemSize itemSize = commonDAO.find(bi.getItemSizeId(), ItemSize.class);
		ItemMaterial itemMaterial = commonDAO.find(bi.getItemMaterialId(), ItemMaterial.class);
		Price price = priceDAO.find(bi.getItemId(), currency.getId());
		OrderItemPrice oiPrice = new OrderItemPrice(bi.getItemId());
		BigDecimal priceMath = null;				
		if (price != null) {
			priceMath = new BigDecimal(price.getPrice());				
			oiPrice.setCurrencyCode(price.getCurrency().getCode());
			oiPrice.setPrice(priceMath);
		}
		BigDecimal discountPrice = UserDiscountService.getDiscountPrice(discount, userDiscount, priceMath);
		discountPrice = discountPrice.setScale(2, RoundingMode.HALF_UP);
		oiPrice.setDiscountPrice(discountPrice);			
		KUser kUser = KUser.instance();
		if (kUser.getUser() != null && isUserDiscounted()) {
			BigDecimal yourPrice = UserDiscountService.getYourPrice(userDiscount, discountPrice);
			yourPrice = yourPrice.setScale(2, RoundingMode.HALF_UP);
			oiPrice.setYourPrice(yourPrice);
		} else {			
			oiPrice.setYourPrice(discountPrice);
		}
		KItemImage img = imageDAO.getMainItemImage(item.getId());
		if (img == null) {
			img = imageDAO.getItemImage(item.getId());				
		}
		BasketItemData data = new BasketItemData(bi,item, itemColor, itemSize, itemMaterial, bi.getQuantity(), oiPrice,img);
		data.setDiscount(discount);
		data.setDelPrice(isUserDiscounted() || data.isDiscounted());
		data.setDelDiscountPrice(isUserDiscounted());
		return data;
	}
	
	public void remove(BasketItemData data) {
		list.remove(data);
		KUser kUser = KUser.instance();
		if (kUser != null) {
			kUser.getBasket().getItems().remove(data.getBasketItem());
		}
		calculateTotal();
		good();
	}
	
	public void save() {
		if (list.isEmpty()) {
			bad("empty_basket");
			return;
		}
		for (BasketItemData biData : list) {
			biData.getBasketItem().setQuantity(biData.getQuantity());
		}
		calculateTotal();
		good();
	}
	
	public void order() {
		KUser kUser = KUser.instance();
		User user = kUser.getUser();
		if (user == null) {
			bad(KConst.MUST_LOGIN);
			return;
		}
		if (KUtil.isEmpty(user.getAddress())) {
			bad(KConst.EMPTY_ACCOUNT_ADDRESS);
			return;
		}
		long sumKadrons = 0;
//		final Date moment = KUtil.getMoment();
		synchronized (kUser.getUser()) {
			if (list.isEmpty()) {
				bad("empty_basket");
				return;
			}
			Long userKadrons = userKadronsDAO.getUserKadrons(user.getId());
			if (userKadrons == null) {
				userKadrons = Long.valueOf(0);
			}
			for (BasketItemData bi : list) {
				OrderItemPrice price = bi.getPrice();
				int q = bi.getQuantity();
				if (price == null 
						|| price.getPrice() == null 
						|| price.getYourPrice() == null 
						|| price.getPrice().doubleValue() <= 0 
						|| price.getYourPrice().doubleValue() <= 0 
						|| q <= 0) {
					bad("error_price_is_not_correct");
					return;
				}
			}
			UserTransaction tx = null;
			try {
				tx = Transaction.instance();
				Order order = new Order(user, currency);
				if (isBonusAvb()) {
					order.setBonusKadrons(kadronDiscount.getBonusKadrons());
				}
				order.setAddress(user.getAddress());
				order = (Order) commonDAO.update(order);
				for (BasketItemData bi : list) {
					OrderItemPrice price = bi.getPrice();
					if (price == null 
							|| price.getPrice() == null 
							|| price.getYourPrice() == null 
							|| price.getPrice().doubleValue() <= 0 
							|| price.getYourPrice().doubleValue() <= 0) {
						bad("error_price_is_not_correct");
						return;
					}
					String itemColor = null;
					String itemSize = null;
					String itemMaterial = null;
					if (bi.getItemColor() != null) {
						itemColor = bi.getItemColor().getColor().getColor().getName(kUser.getKlang());
					}
					if (bi.getItemSize() != null) {						
						itemSize = bi.getItemSize().getSize().getSize().getCode();
					}
					if (bi.getItemMaterial() != null) {
						itemMaterial = bi.getItemMaterial().getMaterial().getMaterial().getName(kUser.getKlang());
					}
					OrderItem item = new OrderItem(order, bi.getItem(),
							itemColor, itemSize,
							itemMaterial, bi.getQuantity(), 
							price.getPrice().doubleValue(), price.getDiscountPrice().doubleValue(), price.getYourPrice().doubleValue(), 
							bi.getItem().getKadrons());
					item = (OrderItem) commonDAO.update(item);
					sumKadrons += (bi.getItem().getKadrons() * bi.getQuantity());
				}	
				//BONUS KADRONS
				if (isBonusAvb()) {
					sumKadrons += kadronDiscount.getBonusKadrons();
				}
				StringBuilder historyMessage = new StringBuilder("ORDERED items in total= "+total);
				//UPDATE USER STATUS & TYPE
				UserType oldType = UserType.valueOf(user.getUserType().toString());
				
				if (user.getUserStatus().equals(UserStatus.REGISTERED)) {
					user.setUserStatus(UserStatus.CONFIRMED);
					historyMessage.append(" , and UserStatus updated to CONFIRMED");
				}
				user = userService.updateStatus(user, userKadrons, sumKadrons);
				kUser.init(user);
				
				userHistoryService.create(user, KConst.ORDER, KConst.ORDER, historyMessage.toString());
				userService.createUserKadrons(user, sumKadrons, "Ordered for kadrons="+sumKadrons);
				Contexts.getEventContext().set("totalKadrons", sumKadrons);
				KMailService.sendOrder();
				tx.commit();
				reset();
				kUser.getBasket().reset();
				if (!oldType.equals(user.getUserType())) {
					message("status_updated", Severity.INFO, user.getUserType().getMessage());
				}
				good();
			} catch (Exception e) {
				e.printStackTrace();
				try {
					tx.rollback();
				} catch (Exception e2) {
					if (log.isErrorEnabled()) {					
						log.error("ERROR on rollback transaction.");
					}
				}
				bad(KConst.BAD_THING);
			}
		}
	}
	
	@Override
	public void changeCurrency(Currency newc) {
		try {
			KUser kUser = KUser.instance();
			if (newc == null || kUser == null) {
				bad(KConst.BAD_THING);
				return;
			} 
			kUser.setCurrency(newc);	
			currency = newc;
			init();
		} catch (Exception e) {
			bad(KConst.BAD_THING);
		}
	}

	public String getConfirmMessage() {
		String message = Interpolator.instance().interpolate("#{message.confirm_order_msg}", new Object());
		return message;
	}

	public Long getTotalKadrons() {
		return totalKadrons;
	}

	public Discount getKadronDiscount() {
		return kadronDiscount;
	}

	public int getUserDiscount() {
		return userDiscount;
	}
	
	public boolean isUserDiscounted() {
		return userDiscount > 0;
	}
	
	public boolean isKadronDiscounted() {
		return kadronDiscount != null;
	}
	
	public boolean isBonusAvb() {
		return isKadronDiscounted() && totalKadrons > kadronDiscount.getKadrons();
	}
	
}